package cn.hd01.weixin.util;

import cn.hd01.util.MD5Util;
import cn.hd01.weixin.model.WxPaySendData;
import com.google.common.collect.Maps;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.service.spi.ServiceException;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.*;

/**
 * @author cyb
 * @date 2017/4/26.
 */
public class WeiXinUtils {
    // 微信统一下单url
    public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    // 微信申请退款url
    public static final String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    // 企业付款
    public static final String TRANSFERS_PAY = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

    // 微信交易类型:公众号支付
    public static final String TRADE_TYPE_JSAPI = "JSAPI";

    // 微信交易类型:公众号支付
    public static final String TRADE_TYPE_NATIVE = "NATIVE";

    // 返回成功字符串
    public static final String RETURN_SUCCESS = "SUCCESS";

    private static final String PREFIX_XML = "<xml>";
    private static final String SUFFIX_XML = "</xml>";
    private static final String PREFIX_CDATA = "<![CDATA[";
    private static final String SUFFIX_CDATA = "]]>";


    public static String nonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String getTimeStamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

    /**
     * 获取微信签名
     *
     * @param map   请求参数集合
     * @param shKey 商户key
     * @return 微信请求签名串
     */
    public static String getSign(Map<String, Object> map, String shKey) {
        StringBuffer sb = new StringBuffer();
        SortedMap sortedMap = new TreeMap(map);
        Set<Map.Entry<String, Object>> set = sortedMap.entrySet();
        Iterator<Map.Entry<String, Object>> iterator = set.iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            //参数中sign、key不参与签名加密
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + shKey);
        return MD5Util.encode(sb.toString()).toUpperCase();
    }

    /**
     * 解析微信服务器发来的请求
     *
     * @param inputStream
     * @return 微信返回的参数集合
     */
    public static SortedMap<String, Object> parseXml(InputStream inputStream) {
        SortedMap<String, Object> map = Maps.newTreeMap();
        try {
            //获取request输入流
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);
            //得到xml根元素
            Element root = document.getRootElement();
            //得到根元素所有节点
            List<Element> elementList = root.elements();
            //遍历所有子节点
            for (Element element : elementList) {
                map.put(element.getName(), element.getText());
            }
            //释放资源
            inputStream.close();
        } catch (Exception e) {
            throw new ServiceException("微信工具类:解析xml异常", e);
        }
        return map;
    }

    /**
     * 扩展xstream,使其支持name带有"_"的节点
     */
    public static XStream xStream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));

    /**
     * 请求参数转换成xml
     *
     * @param data
     * @return xml字符串
     */
    public static String sendDataToXml(WxPaySendData data) {
        xStream.autodetectAnnotations(true);
        xStream.alias("xml", WxPaySendData.class);
        return xStream.toXML(data);
    }

    /**
     * 转化成xml, 单层无嵌套
     *
     * @param map
     * @param isAddCDATA
     * @return
     */
    public static String sendDataToXml(Map<String, Object> map, boolean isAddCDATA) {
        StringBuffer strbuff = new StringBuffer(PREFIX_XML);
        if (map != null && map.size() > 0) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                strbuff.append("<").append(entry.getKey()).append(">");
                if (isAddCDATA) {
                    strbuff.append(PREFIX_CDATA);
                    if (!StringUtils.isEmpty(entry.getValue())) {
                        strbuff.append(entry.getValue());
                    }
                    strbuff.append(SUFFIX_CDATA);
                } else {
                    if (!StringUtils.isEmpty(entry.getValue())) {
                        strbuff.append(entry.getValue());
                    }
                }
                strbuff.append("</").append(entry.getKey()).append(">");
            }
        }
        return strbuff.append(SUFFIX_XML).toString();
    }

    public static String payNotifyReturn(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code
                + "]]></return_code><return_msg><![CDATA[" + return_msg
                + "]]></return_msg></xml>";
    }

}
